#ifndef SWEGL_VECTOR4_H
#define SWEGL_VECTOR4_H

namespace swegl
{

template<typename T> class Vector4
{
public:
	// default contructor, do not initialize components
	Vector4();
	Vector4(T x, T y, T z, T w);
	Vector4(const Vector4 &rhs);
	Vector4(const Vector3<T> &v, T w);

	Vector4 &set(T x, T y, T z, T w);
	Vector4 &operator = (const Vector4 &rhs);
	Vector4 operator - () const;

	T operator [] (uint index) const;
	T &operator [] (uint index);

	void operator += (const Vector4 &rhs);
	void operator -= (const Vector4 &rhs);
	void operator *= (const Vector4 &rhs);
	void operator *= (T s);
	void operator /= (const Vector4 &rhs);
	void operator /= (T s);

	Vector4 operator + (const Vector4 &rhs) const;
	Vector4 operator - (const Vector4 &rhs) const;
	T operator * (const Vector4 &rhs) const;
	Vector4 operator * (T s) const;
	Vector4 operator / (T s) const;

	bool operator == (const Vector4 &rhs) const;
	bool operator != (const Vector4 &rhs) const;
	bool isEqual(const Vector4 &rhs) const;
	bool isEqual(const Vector4 &rhs, const Vector4 &epsilon) const;
	bool isEqual(const Vector4 &rhs, float epsilon) const;
	
	const Vec3 &toVec3() const;
	Vec3 &toVec3();
	
	const T *toPtr() const;
	T *toPtr();

	T length() const;
	T sqrLength() const;

	// clamp v to the range [min..max]
	static Vector4 Clamp(const Vector4 &v, const Vector4 &min, const Vector4 &max);
	// return a new vector made up of the smallest components of v0 and v1
	static Vector4 Minimize(const Vector4 &v0, const Vector4 &v1);
	// return a new vector made up of the largest components of v0 and v1
	static Vector4 Maximize(const Vector4 &v0, const Vector4 &v1);
	// return true if any component is < in v0 than in v1
	static bool IsAnyLess(const Vector4 &v0, const Vector4 &v1);
	// return true if all components are < in v0 than in v1
	static bool IsAllLess(const Vector4 &v0, const Vector4 &v1);
	// return true if any component is <= in v0 than in v1
	static bool IsAnyLessEqual(const Vector4 &v0, const Vector4 &v1);
	// return true if all components are <= in v0 than in v1
	static bool IsAllLessEqual(const Vector4 &v0, const Vector4 &v1);
	// return true if any component is > in v0 than in v1
	static bool IsAnyGreater(const Vector4 &v0, const Vector4 &v1);
	// return true if all components are > in v0 than in v1
	static bool IsAllGreater(const Vector4 &v0, const Vector4 &v1);
	// return true if any component is >= in v0 than in v1
	static bool IsAnyGreaterEqual(const Vector4 &v0, const Vector4 &v1);
	// return true if all components are >= in v0 than in v1
	static bool IsAllGreaterEqual(const Vector4 &v0, const Vector4 &v1);
	// return true if any component is == in v0 and in v1
	static bool IsAnyEqual(const Vector4 &v0, const Vector4 &v1);

	T x;
	T y;
	T z;
	T w;
};

template <typename T> Vector4<T> operator * (T s, const Vector4<T> rhs);

typedef Vector4<float> Vec4;

template<typename T>
inline Vector4<T>::Vector4()
{
}

template<typename T>
inline Vector4<T>::Vector4(T x, T y, T z, T w) : x(x), y(y), z(z), w(w)
{
}

template<typename T>
inline Vector4<T>::Vector4(const Vector4 &rhs) : x(rhs.x), y(rhs.y), z(rhs.z), w(rhs.w)
{
}

template<typename T>
inline Vector4<T>::Vector4(const Vector3<T> &v, T w) : x(v.x), y(v.y), z(v.z), w(w)
{
}

template<typename T>
inline Vector4<T> &Vector4<T>::set(T x, T y, T z, T w)
{
	this->x = x;
	this->y = y;
	this->z = z;
	this->w = w;
	return *this;
}

template<typename T>
inline Vector4<T> &Vector4<T>::operator = (const Vector4 &rhs)
{
	this->x = rhs.x;
	this->y = rhs.y;
	this->z = rhs.z;
	this->w = rhs.w;
	return *this;
}

template<typename T>
inline Vector4<T> Vector4<T>::operator - () const
{
	return Vector4(- this->x, - this->y, - this->z, - this->w);
}

template<typename T>
inline T Vector4<T>::operator [] (uint index) const
{
	return (&x)[index];
}
	
template<typename T>
inline T &Vector4<T>::operator [] (uint index)
{
	return (&x)[index];
}
	
template<typename T>
inline void Vector4<T>::operator += (const Vector4 &rhs)
{
	this->x += rhs.x;
	this->y += rhs.y;
	this->z += rhs.z;
	this->w += rhs.w;
}

template<typename T>
inline void Vector4<T>::operator -= (const Vector4 &rhs)
{
	this->x -= rhs.x;
	this->y -= rhs.y;
	this->z -= rhs.z;
	this->w -= rhs.w;
}

template<typename T>
inline void Vector4<T>::operator *= (const Vector4 &rhs)
{
	this->x *= rhs.x;
	this->y *= rhs.y;
	this->z *= rhs.z;
	this->w *= rhs.w;
}

template<typename T>
inline void Vector4<T>::operator *= (T s)
{
	this->x *= s;
	this->y *= s;
	this->z *= s;
	this->w *= s;
}

template<typename T>
inline void Vector4<T>::operator /= (const Vector4 &rhs)
{
	this->x /= rhs.x;
	this->y /= rhs.y;
	this->z /= rhs.z;
	this->w /= rhs.w;
}

template<typename T>
inline void Vector4<T>::operator /= (T s)
{
	this->x /= s;
	this->y /= s;
	this->z /= s;
	this->w /= s;
}

template<typename T>
inline Vector4<T> Vector4<T>::operator + (const Vector4 &rhs) const
{
	return Vector4(this->x + rhs.x, this->y + rhs.y, this->z + rhs.z, this->w + rhs.w);
}

template<typename T>
inline Vector4<T> Vector4<T>::operator - (const Vector4 &rhs) const
{
	return Vector4(this->x - rhs.x, this->y - rhs.y, this->z - rhs.z, this->w - rhs.w);
}

template<typename T>
inline T Vector4<T>::operator * (const Vector4 &rhs) const
{
	return this->x * rhs.x + this->y * rhs.y + this->z * rhs.z + this->w * rhs.w;
}

template<typename T>
inline Vector4<T> Vector4<T>::operator * (T s) const
{
	return Vector4(this->x * s, this->y * s, this->z * s, this->w * s);
}

template<typename T>
inline Vector4<T> Vector4<T>::operator / (T s) const
{
	return Vector4(this->x / s, this->y / s, this->z / s, this->w / s);
}

template<typename T>
inline bool Vector4<T>::operator == (const Vector4 &rhs) const
{
	return this->x == rhs.x && this->y == rhs.y && this->z == rhs.z && this->w == rhs.w;
}

template<typename T>
inline bool Vector4<T>::operator != (const Vector4 &rhs) const
{
	return this->x != rhs.x || this->y != rhs.y || this->z != rhs.z || this->w != rhs.w;
}

template<typename T>
inline bool Vector4<T>::isEqual(const Vector4 &rhs) const
{
	return x == rhs.x && y == rhs.y && z == rhs.z && w == rhs.w;
}

template<typename T>
inline bool Vector4<T>::isEqual(const Vector4 &rhs, const Vector4 &epsilon) const
{
	return x >= rhs.x - epsilon.x && x <= rhs.x + epsilon.x
		&& y >= rhs.y - epsilon.y && y <= rhs.y + epsilon.y
		&& z >= rhs.z - epsilon.z && z <= rhs.z + epsilon.z
	    && w >= rhs.w - epsilon.w && w <= rhs.w + epsilon.w;
}

template<typename T>
inline bool Vector4<T>::isEqual(const Vector4 &rhs, float epsilon) const
{
	return x >= rhs.x - epsilon && x <= rhs.x + epsilon
		&& y >= rhs.y - epsilon && y <= rhs.y + epsilon
		&& z >= rhs.z - epsilon && z <= rhs.z + epsilon
	    && w >= rhs.w - epsilon && w <= rhs.w + epsilon;
}

template<typename T>
inline const Vec3 &Vector4<T>::toVec3() const
{
	return *reinterpret_cast<const Vec3 *>(&x);
}

template<typename T>
inline Vec3 &Vector4<T>::toVec3()
{
	return *reinterpret_cast<Vec3 *>(&x);
}

template<typename T>
inline const T *Vector4<T>::toPtr() const
{
	return &x;
}

template<typename T>
inline T *Vector4<T>::toPtr()
{
	return &x;
}

template<typename T>
inline T Vector4<T>::length() const
{
	return sqrt(x * x + y * y + z * z + w * w);
}

template<typename T>
inline T Vector4<T>::sqrLength() const
{
	return x * x + y * y + z * z + w * w;
}

template <typename T>
Vector4<T> operator * (T s, const Vector4<T> rhs)
{
	return rhs * s;
}

template<typename T>
inline Vector4<T> Vector4<T>::Clamp(const Vector4 &v, const Vector4 &min, const Vector4 &max)
{
	return Vector4(Clamp(v.x, min.x, max.x), Clamp(v.y, min.y, max.y), Clamp(v.z, min.z, max.z), Clamp(v.w, min.w, max.w));
}

template<typename T>
inline Vector4<T> Vector4<T>::Minimize(const Vector4 &v0, const Vector4 &v1)
{
	return Vector4(Min(v0.x, v1.x), Min(v0.y, v1.y), Min(v0.z, v1.z), Min(v0.w, v1.w));
}

template<typename T>
inline Vector4<T> Vector4<T>::Maximize(const Vector4 &v0, const Vector4 &v1)
{
	return Vector4(Max(v0.x, v1.x), Max(v0.y, v1.y), Max(v0.z, v1.z), Max(v0.w, v1.w));
}

template<typename T>
inline bool Vector4<T>::IsAnyLess(const Vector4 &v0, const Vector4 &v1)
{
	return v0.x < v1.x || v0.y < v1.y || v0.z < v1.z || v0.w < v1.w;
}

template<typename T>
inline bool Vector4<T>::IsAllLess(const Vector4 &v0, const Vector4 &v1)
{
	return v0.x < v1.x && v0.y < v1.y && v0.z < v1.z && v0.w < v1.w;
}

template<typename T>
inline bool Vector4<T>::IsAnyLessEqual(const Vector4 &v0, const Vector4 &v1)
{
	return v0.x <= v1.x || v0.y <= v1.y || v0.z <= v1.z || v0.w <= v1.w;
}

template<typename T>
inline bool Vector4<T>::IsAllLessEqual(const Vector4 &v0, const Vector4 &v1)
{
	return v0.x <= v1.x && v0.y <= v1.y && v0.z <= v1.z && v0.w <= v1.w;
}

template<typename T>
inline bool Vector4<T>::IsAnyGreater(const Vector4 &v0, const Vector4 &v1)
{
	return v0.x > v1.x || v0.y > v1.y || v0.z > v1.z || v0.w > v1.w;
}

template<typename T>
inline bool Vector4<T>::IsAllGreater(const Vector4 &v0, const Vector4 &v1)
{
	return v0.x > v1.x && v0.y > v1.y && v0.z > v1.z && v0.w > v1.w;
}

template<typename T>
inline bool Vector4<T>::IsAnyGreaterEqual(const Vector4 &v0, const Vector4 &v1)
{
	return v0.x >= v1.x || v0.y >= v1.y || v0.z >= v1.z || v0.w >= v1.w;
}

template<typename T>
inline bool Vector4<T>::IsAllGreaterEqual(const Vector4 &v0, const Vector4 &v1)
{
	return v0.x >= v1.x && v0.y >= v1.y && v0.z >= v1.z && v0.w >= v1.w;
}

template<typename T>
inline bool Vector4<T>::IsAnyEqual(const Vector4 &v0, const Vector4 &v1)
{
	return v0.x == v1.x || v0.y == v1.y || v0.z == v1.z || v0.w == v1.w;
}

}

#endif
